//
// Created by edward on 22-11-16.
//

#ifndef CONCURRENCE_QUEUE_V0_H
#define CONCURRENCE_QUEUE_V0_H

#include <mutex>
#include <condition_variable>
#include <queue>

namespace edward::multithread {

    /*!
     * 线程安全的队列，底层使用std::queue进行实现
     * @tparam T 队列的元素类型
     * 使用互斥和条件变量保证线程安全
     */
    template<typename T>
    class Queue {
    public:
        using Type = T;
        Queue() = default;
        ~Queue() = default;
        Queue(const Queue& other) {
            std::lock_guard(other.mtx_);
            queue_ = other.queue_;
        }
        Queue& operator= (const Queue& other) {
            std::scoped_lock lk(mtx_, other.mtx_);  //使用scoped_lock一次加锁，避免死锁
            queue_ = other.queue_;
        }
        void swap(Queue &other) {
            std::scoped_lock lk(mtx_, other.mtx_);
            queue_.swap(other.queue_);
        }
        void push(T t) {
            std::lock_guard lk(mtx_);       //互斥保护数据
            queue_.push(std::move(t));
            cond_.notify_one();                //在临界区内notify
        }
        T pop() noexcept {  //要求T的移动操作不抛出异常——如果T要抛出异常，那么应该考虑转换接口，类似Stack实现。详细内容见《C++并发编程实战》第二版 3.2节相关内容
            T frnt;
            std::unique_lock lk(mtx_);
            cond_.wait(lk, [&](){
                return !queue_.empty();
            });
            frnt = std::move(queue_.front());
            queue_.pop();
            return frnt;
        }
        T tryPop() {    //如果不想阻塞等待，就要处理可能发生的异常，书中是通过返回布尔值，但是我觉得那样有些丑
            T frnt;
            std::lock_guard lk(mtx_);
            if (queue_.empty()) throw std::logic_error("pop a empty queue!!!");
            frnt = std::move(queue_.front());
            queue_.pop();
            return frnt;
        }
        bool empty() const {
            std::lock_guard lk(mtx_);
            return queue_.empty();
        }
        int size() const {
            std::lock_guard lk(mtx_);
            return queue_.size();
        }

    private:
        mutable std::mutex mtx_;
        mutable std::condition_variable cond_;
        std::queue<T> queue_;
    };

}


#endif //CONCURRENCE_QUEUE_V0_H
